/*****************************************************************
* Avetis Petrosyan
* Rolando Douglas
* ECE 625
* Project Digital Scoreboard
* General commands to the 68HCS12 along with functions to simplify
* interfacing with the DRAGON12+ board
******************************************************************/

#include <hidef.h>      /* common defines and macros */
#include <string.h>
#include "derivative.h" /* derivative-specific definitions */
#include "commands.h"


#define G_LED 0x7D
#define O_LED 0x3F
#define A_LED 0x77
#define L_LED 0x38

#define U_LED 0x3E
#define C_LED 0x39
#define H_LED 0x76

// Cursor position both line and index
unsigned char g_ucCursorPos[2];


void MSDelay(unsigned int itime)
{
  unsigned int i; unsigned int j;
  for(i=0;i<itime;i++)
  for(j=0;j<4000;j++);
}


// Send a command or data to the LCD
// PORTK is used to interface with LCD
// LCD Module is in 4-bit mode.
// Port K bit assignment:
//   7 = unused
//   6 = unused
//   5 = Module DB7
//   4 = Module DB6
//   3 = Module DB5
//   2 = Module DB4
//   1 = EN (pulse 1 to write)
//   0 = RS (0=command, 1=data) 
//
//and 
//
//   7 = unused
//   6 = unused
//   5 = Module DB3
//   4 = Module DB2
//   3 = Module DB1
//   2 = Module DB0
//   1 = EN (pulse 1 to write)
//   0 = RS (0=command, 1=data) 
//
void CmdToLCD(unsigned char _ucCommand, unsigned char _ucRS)
{
   unsigned char cDB7toDB4;
   unsigned char cDB3toDB0;

   // Shift high nibble to center of byte for Pk5-Pk2 (DB7-DB4)       
   cDB7toDB4 = (_ucCommand & 0xF0) >> 2;
   // Clear bits Pk5-Pk2 (DB7-DB4)
   LCD_DATA = LCD_DATA & ~0x3C;
   // Set Pk5-Pk2 (DB7-DB4) to PORTK  
   LCD_DATA = LCD_DATA | cDB7toDB4;   
   // Delay 1ms
   MSDelay(1);

   if(_ucRS == RS_COMMAND_MODE) 
   {
      // Set RS to command mode (RS = 0)
      LCD_CTRL = LCD_CTRL & ~RS;
   } 
   else if(_ucRS == RS_DATA_MODE)
   {
      // Set RS to data mode (RS = 1)
      LCD_CTRL = LCD_CTRL | RS; 
   }
    
   // Delay 1ms
   MSDelay(1);
   // Raise enable "EN" (pulse 1 to write)
   LCD_CTRL = LCD_CTRL | EN;
   // Delay 5ms
   MSDelay(5);
   // Drop enable "EN" to capture command
   LCD_CTRL = LCD_CTRL & ~EN;
   // Delay 15ms
   MSDelay(15);                

   // Shift low nibble to center of byte for Pk5-Pk2 (DB3-DB0)
   cDB3toDB0 = (_ucCommand & 0x0F) << 2;
   // Clear bits Pk5-Pk2 (DB3-DB0)
   LCD_DATA = LCD_DATA & ~0x3C; 
   // Send low nibble to PORTK
   LCD_DATA = LCD_DATA | cDB3toDB0;     
   // Raise enable "EN" (pulse 1 to write)
   LCD_CTRL = LCD_CTRL | EN;   
   // Delay 5ms
   MSDelay(5);
   // Drop enable "EN" to capture command
   LCD_CTRL = LCD_CTRL & ~EN;
   // Delay 15ms
   MSDelay(15); 
}


// Initialize LCD
void InitializeLCD(void)
{     
   DDRK = 0xFF;   
   // Reset sequence provided by data sheet
   CmdToLCD(0x33, RS_COMMAND_MODE);   
   MSDelay(1);
   // Reset sequence provided by data sheet
   CmdToLCD(0x32, RS_COMMAND_MODE);   
   MSDelay(1);
   // Function set to four bit data length
   // 2 line, 5 x 7 dot format
   CmdToLCD(0x28, RS_COMMAND_MODE);   
   MSDelay(1);
   // Entry mode set, increment, no shift
   CmdToLCD(0x06, RS_COMMAND_MODE);  
   MSDelay(1);
   // Display set, disp on, cursor off, blink off
   CmdToLCD(0x0C, RS_COMMAND_MODE);  
   MSDelay(1);
   
   ClearLCD();
}


void ClearLCD(void)
{
   // Clear display
   CmdToLCD(0x01, RS_COMMAND_MODE);  
   MSDelay(1);
   // Set start posistion, home position
   CmdToLCD(0x80, RS_COMMAND_MODE);    
   MSDelay(1);
   // Set cursor position to Line 1, Index 0 (home position)
   UpdateCursorPos(LINE_1, 0);
}


// Initialize variables
void InitializeVariables(void)
{
   memset(&g_ucCursorPos, 0, sizeof(g_ucCursorPos));
}


void SetCursorLine(unsigned char _ucLine)
{
   // If the cursor is on the correct line, return
   if(g_ucCursorPos[CURSOR_LINE] == _ucLine) 
      return;
   
   // Change cursor to appropriate line
   (_ucLine == LINE_1) ? CmdToLCD(0x80, RS_COMMAND_MODE) : CmdToLCD(0xC0, RS_COMMAND_MODE); 
   
   // Store new line position in global variable
   g_ucCursorPos[CURSOR_LINE] = _ucLine;
   // Set cursor to index 0 since the line was changed
   g_ucCursorPos[CURSOR_INDEX] = 0;
}


void SetCursorIndex(unsigned char _ucIndex)
{
   unsigned char ucShiftCmd = 0;
   char cShiftDirection = 0;

   // If the index is in the right place, return
   if(_ucIndex == g_ucCursorPos[CURSOR_INDEX])
   {
      return;
   }  
   // If the desired index is to the left of the current position 
   else if(_ucIndex < g_ucCursorPos[CURSOR_INDEX])
   {                         
      ucShiftCmd = SHIFT_LEFT_CMD;
      cShiftDirection = -1;
   }
   else // If the desired index is to the right of the current position
   {        
      ucShiftCmd = SHIFT_RIGHT_CMD;
      cShiftDirection = 1;
   }                    
    
   
   do
   {        
      // Shift the cursor to the right or left by 1
      CmdToLCD(ucShiftCmd, RS_COMMAND_MODE);
      // Delay 1ms
      MSDelay(1);
      // Increment or decrement the cursor position depending on the shift
      g_ucCursorPos[CURSOR_INDEX] = g_ucCursorPos[CURSOR_INDEX] + cShiftDirection;
   }while((g_ucCursorPos[CURSOR_INDEX] != _ucIndex) && (g_ucCursorPos[CURSOR_INDEX] <= MAX_LCD_SIZE));             
}


void UpdateCursorPos(unsigned char _ucLine, unsigned char _ucIndex)
{
   g_ucCursorPos[CURSOR_LINE] = _ucLine;   
   g_ucCursorPos[CURSOR_INDEX] = _ucIndex;   
}


// Set the cursor position from the current cursor position
void SetCursorPosition(unsigned char _ucLine, unsigned char _ucIndex) 
{
    SetCursorLine(_ucLine);
    SetCursorIndex(_ucIndex);
}
   
   
// Write a string to specified line
void WriteString(unsigned char *_ucString, unsigned char _ucLine, unsigned char _ucIndex)
{
   int i = 0;

   SetCursorPosition(_ucLine, _ucIndex);
  
   for(i = 0; i < strlen(_ucString); i++) 
   {
      // Delay 1ms
      MSDelay(1);
      // Send characters
      CmdToLCD(_ucString[i], RS_DATA_MODE);      
   } 

   // Update cursor position   
   UpdateCursorPos(_ucLine, _ucIndex + strlen(_ucString));
}


// Write a string to specified line
void WriteChar(unsigned char _ucNum, unsigned char _ucLine, unsigned char _ucIndex)
{
   // Cursor position
   SetCursorPosition(_ucLine, _ucIndex);
   // Delay 1ms
   MSDelay(1);
   // Send character (adding to ASCII 0 so we can send the regular number)
   CmdToLCD(_ucNum, RS_DATA_MODE);     
   // Update cursor position
   UpdateCursorPos(_ucLine, _ucIndex + 1);
}
  
  
void WriteGoal7SegLED(int _nTime)  
{
   int i = 0;
   int nDelay = 8;
      
   // PORTB as output since LEDs are connected to it
   DDRB = 0xFF;    
   DDRP = 0xFF;   
         
   for(i = 0; i < _nTime; i++)
   {

      PTP = 0x0E;   
      PORTB = G_LED;
      MSDelay(nDelay);             
           
      PTP = 0x0D;      
      PORTB = O_LED;
      MSDelay(nDelay);        
       
      PTP = 0x0B;
      PORTB = A_LED;
      MSDelay(nDelay);        
         
      PTP = 0x07;   
      PORTB = L_LED;
      MSDelay(nDelay);      
   }
   
   // Turn off the display   
   DDRB = 0x00;   
}
   
   
void WriteNoGoal7SegLED(int _nTime)  
{
   int i = 0;
   int nDelay = 8;
   

   DDRB = 0xFF;    //PORTB as output since LEDs are connected to it
   DDRP = 0xFF;   
         
   for(i = 0; i < _nTime; i++)
   {
      PTP = 0x0E;   
      PORTB = O_LED;
      MSDelay(nDelay);             
           
      PTP = 0x0D;      
      PORTB = U_LED;
      MSDelay(nDelay);        
       
      PTP = 0x0B;
      PORTB = C_LED;
      MSDelay(nDelay);        
         
      PTP = 0x07;   
      PORTB = H_LED;
      MSDelay(nDelay);      
   }
   
   // Turn off the display   
   DDRB = 0x00;   
}   
   

void WriteNoGoal(int _nTime, unsigned char *_ucaTeam)
{
   unsigned char ucaNoGoal[] = ":( OUCH :(";
  
   WriteNoGoal7SegLED(_nTime);
   ClearLCD();
   WriteString(_ucaTeam, LINE_1, 0);
   WriteNoGoal7SegLED(_nTime);         
   WriteString(ucaNoGoal, LINE_2, 0);
   WriteNoGoal7SegLED(_nTime);
   ShiftWords(SHIFT_WORD_LEFT_CMD);
}


void WriteGoal(int _nTime, unsigned char *_ucaTeam)
{
   unsigned char ucaGoal[] = "GGOOOOOOOALLL!!!";
  
   WriteGoal7SegLED(_nTime);
   ClearLCD();
   WriteString(_ucaTeam, LINE_1, 0);
   ClearLCD();
   WriteString(_ucaTeam, LINE_2, 0);
   WriteGoal7SegLED(_nTime);   
   ClearLCD();
   WriteString(_ucaTeam, LINE_1, 0);   
   WriteString(ucaGoal, LINE_2, 0);
   WriteGoal7SegLED(_nTime);
   ShiftWords(SHIFT_WORD_LEFT_CMD);
}
  

void ShiftWords(unsigned char _ucShift)
{
   int i = 0;
   int nDelay = 15;
    
   for(i = 0; i < MAX_LCD_SIZE; i++)   
   {
      CmdToLCD(_ucShift, RS_COMMAND_MODE);
      MSDelay(nDelay);
   }   
}


void WriteNumber(unsigned char _ucNum, unsigned char _ucLine, unsigned char _ucIndex, unsigned char _ucZeroPadding)
{
   unsigned char ucMSB = 0;
   unsigned char ucLSB = 0;
   
     
   if(_ucNum < 10)      
   {
      if(_ucZeroPadding == ZERO_PADDING)
      {
         // Write a leading 0
         WriteChar('0', _ucLine, _ucIndex);
         
         // Increment index to next index since there is a 0 in front
         _ucIndex++;
      }      
      WriteChar(_ucNum + '0', _ucLine, _ucIndex);
   }
   else
   {
      // Example:
      
      // _ucNum = 58
      // ucLSB = 58 % 10 = 8
      // _ucNum = 58 / 10 = 5
      // ucMSB = 5 % 10 = 5 // doing last step for correctness
      
      ucLSB = _ucNum % 10;       
      _ucNum = _ucNum / 10;      
      ucMSB = _ucNum % 10;
      
      WriteChar(ucMSB + '0', _ucLine, _ucIndex);
      WriteChar(ucLSB + '0', _ucLine, _ucIndex + 1);
   }
      
}  
  
void WriteTime(unsigned char _ucMin, unsigned char *_ucPrevMin, unsigned char _ucWriteMin, unsigned char _ucSec)
{
   // Only overwrite minute if it has changed
   if(*_ucPrevMin != _ucMin || _ucWriteMin == UPDATE_MIN)
   {
      WriteNumber(_ucMin, LINE_1, MIN_INDEX, ZERO_PADDING);           
   }                                               
   WriteNumber(_ucSec, LINE_1, SEC_INDEX, ZERO_PADDING);
   
   // Store previous minute
   *_ucPrevMin = _ucMin;
} 

void DecreaseTime(unsigned char *_ucTime)
{
   // Decrease the time 20 minutes at a time
   if(*_ucTime > 20)
   {
      *_ucTime -= 20;
   }
   else // if it's 20 or less, set the time to 0
   {
      *_ucTime = 0;
   }
}

int DetermineWinner(unsigned char *_ucaTeam1, unsigned char *_ucaTeam2, unsigned char _ucTeam1Score, unsigned char _ucTeam2Score)
{
   unsigned char ucaTie[] = "TIED";
   unsigned char ucaWinner[] = "WINNER";
   int nTie = 0;
   
   
   // Clear screen
   ClearLCD();
   
   
   if(_ucTeam1Score > _ucTeam2Score)
   {
      // Team 1 won      
      WriteString(ucaWinner, LINE_1, 5);
      WriteString(_ucaTeam1, LINE_2, 4);
      
   }
   else if(_ucTeam1Score == _ucTeam2Score)
   {
      // Tie
      WriteString(ucaTie, LINE_1, 6);      
      nTie = TIE_GAME;
   }
   else
   {
      // Team 2 won
      WriteString(ucaWinner, LINE_1, 5);
      WriteString(_ucaTeam2, LINE_2, 3);
   }
   
   return nTie;
}